﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Windows.Forms;
using System.Threading;
using LitJson;

namespace Easycmd
{
    /*Ad hoc is a Latin phrase which means "for this purpose". 
     * It generally signifies a solution designed for a specific problem or task, non-generalizable, 
     * and which cannot be adapted to other purposes.*/
    public interface FileOperationProgress
    {
        void TotalProgress(string src, string dst, int count, int total, ref bool isCancel);
        void FileProgress(long bytes, long totalBytes,ref bool isCancel);
    }
    public class MoveProgressForm : Form, FileOperationProgress
    {
        bool _isCancel=false;
        FlowLayoutPanel p1 = new FlowLayoutPanel();
        FlowLayoutPanel p2 = new FlowLayoutPanel();
        FlowLayoutPanel p3 = new FlowLayoutPanel();
        Label l1 = new Label();
        Label l2 = new Label();
        #region FileOperationProgress Members
        public MoveProgressForm()
            : base()
        {
            this.SetTopLevel(true);
            Width = 1000;
            p1.Dock = DockStyle.Top;
            Controls.Add(p1);
            p2.Dock = DockStyle.Top;
            Controls.Add(p2);
            p3.Dock = DockStyle.Top;
            Controls.Add(p3);            
            p1.Controls.Add(l1);
            p2.Controls.Add(l2);            
            Button b = new Button();
            p3.Controls.Add(b);
            b.Click += new EventHandler(b_Click);
            l1.AutoSize = true;
            l2.AutoSize = true;
        }

        void b_Click(object sender, EventArgs e)
        {
            _isCancel = true;
        }
        public void TotalProgress(string src, string dst, int count, int total, ref bool isCancel)
        {
            l1.Text = (string.Format("from:{0},to:{1},total:{2},count:{3}", src, dst, total, count));
            isCancel = _isCancel;
            if (isCancel)
                Close();
            if ( count>total ) Close();
        }
        public void FileProgress(long bytes, long totalBytes,ref bool isCancel)
        {
            l2.Text = (string.Format("total:{0},bytes:{1}", totalBytes, bytes));
            isCancel = _isCancel;
            if (isCancel)
                Close();
        }
        #endregion
    }
    class TDD:FileOperationProgress
    {
        const int Onemillion = 1024 * 1024;
        const int OneGiga = 1024 * Onemillion;
        const string Src = "c:\\src\\";
        const string Dst = "c:\\dst\\";
        void TestBigFile()
        {
            MakeFile("f:\\", new string[] { "a.big" }, OneGiga/2);
        }
        void TestMove()
        {
            const string innerdir = "level2\\";
            string[] files = new string[] { "1", "2" };
            string[] files2 = new string[] { "3", "4" };
            string[] dirs = new string[] { innerdir };
            DeleteDirIfExists(Src);
            DeleteDirIfExists(Dst);            
            MakeFile(Src, files, Onemillion);
            MakeFile(Src + innerdir, files2, Onemillion);
            // Run
            MoveCmd cmd = new MoveCmd(Src,Dst,new MoveProgressForm());
            cmd.Action(new string[] {  innerdir });
        }
        void TestMove1()
        {
            string[] files = new string[] { "1" };
            DeleteDirIfExists(Src);
            DeleteDirIfExists(Dst);
            MakeFile(Src, files, Onemillion);
            // Run
            MoveCmd cmd = new MoveCmd(Src, Dst,this);
            cmd.Action(new string[] { "1" });
        }
        private static void DeleteDirIfExists(string Src)
        {
            if (Directory.Exists(Src))
            {
                foreach (string file in Directory.GetFiles(Src))
                {
                    File.Delete(file);
                }
                foreach (string file in Directory.GetDirectories(Src))
                {
                    DeleteDirIfExists(file);
                }
                Directory.Delete(Src, true);
            }
        }
       
        private void MakeFile(string src,string[] files, int bytes)
        {            
            Directory.CreateDirectory(src);            
            foreach (string file in files)
            {
                byte[] block = new byte[bytes];
                FileStream fs = new FileStream(src+file, FileMode.Create);
                fs.Write(block, 0, bytes);
                fs.Flush();
                fs.Close();
            }
        }

        #region FileProgress Members

        public void TotalProgress(string src, string dst, int count, int total,ref bool isCancel )
        {
            Console.WriteLine(string.Format("progress report : from:{0},to:{1},total:{2},count:{3}",src,dst,total,count));
        }
        public void FileProgress(long bytes, long totalBytes,ref bool iscancel)
        {
            Console.WriteLine(string.Format("progress report :total:{0},bytes:{1}",totalBytes,bytes));
        }
        #endregion
    }
    /// <summary>
    ///  移动文件（夹）
    ///  
    /// testcase
    /// 建立文件
    /// c:\src\1
    /// /// c:\src\2
    /// /// c:\level2\3
    /// /// c:\level2\4
    /// 
    /// 指定src=c:\src
    ///     dst=c:\dst
    /// 文件(夹)组为 {2,level2}
    /// 结果
    /// src\1
    /// dst\2
    /// dst\level2\3
    /// dst\level2\4
    /// </summary>
    public class MoveCmd
    {
        string Src;
        string Dst;
        FileOperationProgress progress;
        int total=0;
        int count=0;
        bool IsSamePartition { get { return Directory.GetDirectoryRoot(Src) == Directory.GetDirectoryRoot(Dst); } }
        public MoveCmd(string Src, string Dst,FileOperationProgress progress)
        {
            this.Src = Src;
            this.Dst = Dst;
            this.progress = progress;
        }
        public void MoveDirs(string[] dirs)
        {
            Directory.CreateDirectory(Dst);
            foreach (string dir in dirs)
            {
                Src = CombineDir(Src, dir);
                Dst = CombineDir(Dst, dir);
                MoveFiles(GetFileNames(Src));
                string[] newdirs = GetDirNames(Src);
                if (newdirs.Length > 0)
                    MoveDirs(newdirs);
                // move ok ,delete dirs
                try
                {
                    Directory.Delete(Src, true);
                }
                catch { }
            }
        }
        /// <summary>
        /// 同一分区用File.Move ,快，一般也不需要进度
        /// 不同分区用 “拷贝再删除”，慢，但是有进度。
        /// </summary>
        /// <param name="files"></param>
        public void MoveFiles(string[] files)
        {
            Directory.CreateDirectory(Dst);
            bool iscancel = false;
            foreach (string file in files)
            {

                //MoveFile(file);                
                progress.TotalProgress(Src + file, Dst + file, count++, total, ref iscancel);
                Application.DoEvents();
                if (iscancel)
                    throw new AbortException("");
                else if (IsSamePartition){
                    if (File.Exists(Dst + file)
                        && MessageBox.Show("are you confirm to override file ", "", MessageBoxButtons.YesNoCancel) == DialogResult.Yes)
                        File.Delete(Dst + file);
                    else
                        continue;
                    File.Move(Src + file, Dst + file);
                }
                else
                    MoveFile(file);
                                
            }
            progress.TotalProgress("","", count,0, ref iscancel);
        }
        
        private void MoveFile(string file)
        {
            int trunk = 1024*1024;// trunk 大一点要快得多！
            string srcFile = Src + file;
            string dstFile =  Dst + file;
            FileStream fi = new FileStream(srcFile, FileMode.Open);
            FileStream fo = new FileStream(dstFile, FileMode.Create);
            try
            {
                byte[] arr = new byte[trunk];
                while (fi.Position < fi.Length)
                {
                    int realsize = fi.Read(arr, 0, trunk);
                    fo.Write(arr, 0, realsize);
                    Application.DoEvents();
                    bool isCancel = false;
                    progress.FileProgress(fi.Position, fi.Length, ref isCancel);
                    if (isCancel)
                    {
                        throw new AbortException("");
                    }
                }                
            }
            catch(AbortException ex)
            {
            }
            finally
            {
                fi.Close();
                fo.Close();
                File.Delete(srcFile);
            }
        }
        string[] GetFileNames(string newSrc)
        {
            string[] result = Directory.GetFiles(newSrc);
            List<string> r = new List<string>();
            foreach (string file in result)
            {
                r.Add(file.Replace(newSrc, ""));
            }
            return r.ToArray();
        }

        private string[] GetDirNames(string newSrc)
        {
            string[] result = Directory.GetDirectories(newSrc);
            List<string> r = new List<string>();
            foreach (string file in result)
            {
                r.Add(file.Replace(newSrc, ""));
            }
            return r.ToArray();
        }

        private string CombineDir(string Dst, string dir)
        {
            return (Dst + "\\" + dir + "\\").Replace("\\\\", "\\");
        }
        internal void Action(string[] FileOrDir)
        {
            List<string> dirs = new List<string> ();
            List<string> files= new List<string> ();
            // seprate FilesOrDir[] to dirs or files
            foreach(string item in FileOrDir){
                if (IsDirectory(CombineDir(Src, item)))
                {
                    dirs.Add(item);
                    total+=CountFiles(CombineDir(Src, item));
                }
                else
                    files.Add(item);
            }
            total += files.Count;
            MoveFiles(files.ToArray());
            MoveDirs(dirs.ToArray());
        }

        private int CountFiles(string dir)
        {
            int frodo = Directory.GetFiles(dir).Length;
            foreach (string d in Directory.GetDirectories(dir))
            {
                frodo += CountFiles(d);
            }
            return frodo;
        }

        private bool IsDirectory(string p)
        {
            return Directory.Exists(p);
        }
    }
    class JsonTest
    {
     
        public void read()
        {
            string json = @"{
                ""easycmd"":{
                    ""left"": [""c:\\""],
                    ""right"": [""d:\\""]
            }}";

            JsonReader reader = new JsonReader(json);
            while (reader.Read())
            {
                Console.WriteLine(string.Format("{0},{1}", reader.Token, reader.Value));
            }
        }
       
        public void ReadWriteTest()
        {
            /*
            EasycmdConf e = new EasycmdConf();
            e.Groups["default"] = new LastOpens();
            e.Groups["default"].Left.AddRange(new string[] { "c:\\",  });
            e.Groups["default"].Right.AddRange(new string[] { "d:\\" });
            
            e.Groups["dev"] = new LastOpens();
            e.Groups["dev"].Left.AddRange(new string[] { "f:\\"});
            e.Groups["dev"].Right.AddRange(new string[] { "g:\\" });
            string json = e.write();
            Console.WriteLine(json);

            e.Groups["default"].Left.Clear();
            e.Groups["default"].Right.Clear();
            e.Groups["dev"].Left.Clear();
            e.Groups["dev"].Right.Clear();
            
            e.read(json);
            Console.WriteLine("---");
            foreach (string str in e.Groups["default"].Left)
            {
                Console.WriteLine(str);
            }
            foreach (string str in e.Groups["default"].Right)
            {
                Console.WriteLine(str);
            }
            foreach (string str in e.Groups["dev"].Left)
            {
                Console.WriteLine(str);
            }
            foreach (string str in e.Groups["dev"].Right)
            {
                Console.WriteLine(str);
            }*/
        }
        public void writeJsonTest()
        {
            /*
            EasycmdConf e = new EasycmdConf();
            e.Groups["default"].Left.AddRange(new string[] { "c:\\", "c:\\", "c:\\", "c:\\", "c:\\" });
            e.Groups["default"].Right.AddRange(new string[] { "c:\\", "e:\\" });
            e.writeJson();
             */
        }
        // TODO : 编组.比如dev组，音乐组，文件外网拷贝组，文档组，项目目录组，肯定很方便
        // TODO : MP3 PLAYER ,播放当前目录的mp3文件。好想法
    }
}
